'use strict';
const path = require('path');
const jimp = require('jimp');
const { createCanvas } = require('canvas');
const fs = require('fs');
const Utils = require('../../../lib/utils');

const downloadpath = path.join(__dirname, './files/downloadFile/');
const ctrImgPath = path.join(__dirname, './files/imgsCliped/');
const imgFolder = path.join(__dirname, './files/imgBasic/');
const imgFileList = fs.readdirSync(imgFolder);
const filterImgList = imgFileList.filter(item => {
    const reg = /(.*)\.(jpg|bmp|gif|ico|pcx|jpeg|tif|png|raw|tga)$/;
    return reg.test(item);
});
const imgList = filterImgList;

const canvas = createCanvas(200, 200);
const ctx = canvas.getContext('2d');
let retryNum = 0;

class Page2RegistertCreateAccount extends Page {

    async onCheck() {
        return await this.checkElement('#cvf-arkose-js-elements');
    }

    async crop(x, y) {
        const files = fs.readdirSync(downloadpath);
        if (files) {
            const image = await jimp.read(files[0]);
            image.crop(x, y, 100, 100);
            await image.writeAsync(`./files/imgsCliped/${(x / 100 + 1) + 3 * y / 100}.jpg`);
        }
    }

    // 缩小图片尺寸处理
    async shrinkingImg(folder, imgList = []) {
        const list = await Promise.all(imgList.map(async item => {
            const oImg = await this.loadImage(folder + '/' + item);
            const imgWidth = 8;
            ctx.clearRect(0, 0, imgWidth, imgWidth);
            ctx.drawImage(oImg, 0, 0, imgWidth, imgWidth);
            const data = ctx.getImageData(0, 0, imgWidth, imgWidth);
            return data.data;
        }));
        return list;
    }

    // 灰度化-比较灰度值
    async getHashList(folder, imgList) {
        const list = await this.shrinkingImg(folder, imgList);
        const averageList = [];
        list.forEach(item => {
            const itemList = [];
            item.forEach((newItem, index) => {
                if ((index + 1) % 4 === 0) {
                    const newItem1 = item[index - 3];
                    const newItem2 = item[index - 2];
                    const newItem3 = item[index - 1];
                    const gray = (newItem1 + newItem2 + newItem3) / 3;
                    itemList.push(gray);
                }
            });
            const hashData = this.getHash(itemList);
            averageList.push(hashData);
        });
        return averageList;
    }

    getHash(arr) {
        const length = arr.length;
        const average = arr.reduce((pre, next) => pre + next, 0) / length;
        // return arr.map(item => item >= average ? 1 : 0).join('');
        return arr.map(item => {
            if (item >= average) {
                return 1;
            }
            return 0;
        }).join('');
    }

    strSimilarity2Number(s, t) {
        const n = s.length,
            m = t.length,
            d = [];
        let i,
            j,
            s_i,
            t_j,
            cost;
        if (n === 0) return m;
        if (m === 0) return n;
        for (i = 0; i <= n; i++) {
            d[i] = [];
            d[i][0] = i;
        }
        for (j = 0; j <= m; j++) {
            d[0][j] = j;
        }
        for (i = 1; i <= n; i++) {
            s_i = s.charAt(i - 1);
            for (j = 1; j <= m; j++) {
                t_j = t.charAt(j - 1);
                if (s_i === t_j) {
                    cost = 0;
                } else {
                    cost = 1;
                }
                d[i][j] = this.Minimum(d[i - 1][j] + 1, d[i][j - 1] + 1, d[i - 1][j - 1] + cost);
            }
        }
        return d[n][m];
    }

    Minimum(a, b, c) {
        return a < b ? (a < c ? a : c) : (b < c ? b : c);
    }

    strSimilarity2Percent(s, t) {
        const l = s.length > t.length ? s.length : t.length;
        const d = this.strSimilarity2Number(s, t);
        return (1 - d / l).toFixed(4);
    }

    async getSimilarImgList(frame2, num) {
        return new Promise(async resolve => {
            const enterCheckStr = await frame2.waitForSelector('#game_children_challenge', { timeout: 3000 }).catch(() => {
                resolve(num);
            });
            // 下载验证码图片
            if (enterCheckStr) {
                const imgUrl = await frame2.$eval('#game_challengeItem_image', img => img.getAttribute('src'));
                if (imgUrl) {
                    const base64 = imgUrl.replace(/^data:\w+\/.*;base64,/, '');
                    const dataBuffer = Buffer.from(base64, 'base64');
                    fs.writeFileSync(downloadpath + Date.now() + '.jpg', dataBuffer);
                    // 裁剪
                    for (let y = 0; y < 2; y++) {
                        for (let x = 0; x < 3; x++) {
                            await this.crop(x * 100, y * 100);
                        }
                    }
                    // 计算相似度
                    const arr = await this.getHashList(imgFolder, imgList);
                    const ctrImgFolder = path.resolve(__dirname, ctrImgPath);
                    const ctrImgList = fs.readdirSync(ctrImgFolder);
                    const ctrArr = await this.getHashList(ctrImgFolder, ctrImgList);
                    let maxImg = 0;
                    let resImg;
                    for (let i = 0, len = ctrArr.length; i < len; i++) {
                        let maxPer = 0;
                        const ctrEle = ctrArr[i];
                        for (let index = 0, length = arr.length; index < length; index++) {
                            const element = arr[index];
                            const percent = this.strSimilarity2Percent(element, ctrEle);
                            // console.log('相似度: ', percent);
                            if (percent > maxPer) {
                                maxPer = percent;
                            }
                        }
                        // console.log('maxPer: ', maxPer);
                        if (maxPer >= maxImg) {
                            maxImg = maxPer;
                            resImg = i;
                        }
                    }
                    // console.log(`resImg: ${resImg}, maxImg: ${maxImg}`);
                    const a = await frame2.$x(`//*[@id="game_children_challenge"]/div/a[${resImg}]`);
                    await a[0].click();
                    await Utils.sleep(2000);
                    num += 1;
                }
                resolve(num);
            }
        });
    }

    async onStart(frame2 = null, num = 0) {
        if (retryNum === 0) {
            this.logger.info('图片验证码页面');
            // 切换到iframe
            await this.waitForSelector('iframe');
            const elementHandle = await this.$('iframe');
            const frame = await elementHandle.contentFrame();

            await frame.waitForSelector('iframe');
            const elementHandle1 = await frame.$('iframe');
            const frame1 = await elementHandle1.contentFrame();

            await frame1.waitForSelector('iframe');
            const elementHandle2 = await frame1.$('iframe');
            frame2 = await elementHandle2.contentFrame();
            await frame2.waitForSelector('#home_children_button');

            await frame2.click('#home_children_button');
        }
        // 识别验证码并点击
        await this.getSimilarImgList(frame2, num)
            .then(n => {
                if (n > num) {
                    num = n;
                    // console.log(`第${n}次点击`);
                    return this.getSimilarImgList(frame2, n);
                }
            })
            .then(n => {
                if (n > num) {
                    num = n;
                    // console.log(`第${n}次点击`);
                    return this.picCaptch(frame2, n);
                }
            })
            .then(n => {
                console.log(`第${n}次点击`);
            });
        await this.waitForSelector('#verification-code-form', { timeout: 5000 }).catch(async () => {
            retryNum += 1;
            // console.log(`图片验证码未通过: 第${retryNum}次重试.`);
            await frame2.click('#wrong_children_button');
            await Utils.sleep(2000);
            await this.onStart(frame2, 0);
        });
    }
    async onFinish() {
        await Utils.delFile(downloadpath);
        return true;
    }

}

module.exports = Page2RegistertCreateAccount;
